package org.sith.taskrunner.ejb.services.impl;


import org.sith.taskrunner.api.toolkit.TaskToolkit;
import org.sith.taskrunner.api.user.Role;
import org.sith.taskrunner.ejb.ToolkitClassCreator;
import org.sith.taskrunner.ejb.annotations.AllowedRoles;
import org.sith.taskrunner.ejb.db.entity.TaskToolkitEntity;
import org.sith.taskrunner.ejb.exceptions.TaskToolkitRegisterException;
import org.sith.taskrunner.ejb.interceptors.PermissionsChecker;
import org.sith.taskrunner.ejb.interceptors.SynchronizationInterceptor;
import org.sith.taskrunner.ejb.services.TaskToolkitRegisterService;

import javax.ejb.Stateful;
import javax.interceptor.Interceptors;
import java.io.Serializable;
import java.util.*;

@Interceptors({PermissionsChecker.class, SynchronizationInterceptor.class})
@Stateful(name = "TaskToolkitRegisterServiceEJB")
public class TaskToolkitRegisterServiceBean implements TaskToolkitRegisterService, Serializable {

    private final static Map<String, TaskToolkit> activeToolkits = new HashMap<String, TaskToolkit>();
    private final static Map<String, TaskToolkit> disabledToolkits = new HashMap<String, TaskToolkit>();

    public TaskToolkitRegisterServiceBean() {
    }


    @Override
    public void registerToolkitBuilder(TaskToolkitEntity taskToolkit) throws TaskToolkitRegisterException {
        checkIsToolkitBuilderExists(taskToolkit.getToolkitName());
        try {
            disabledToolkits.put(taskToolkit.getToolkitName(), ToolkitClassCreator.create(taskToolkit.getToolkitClazz()).newInstance());
        } catch (IllegalAccessException e) {
            throw new TaskToolkitRegisterException(e);
        } catch (InstantiationException e) {
            throw new TaskToolkitRegisterException(e);
        }
    }

    private void checkIsToolkitBuilderExists(String toolkitBuilderName) throws TaskToolkitRegisterException {
        if (activeToolkits.containsKey(toolkitBuilderName) || disabledToolkits.containsKey(toolkitBuilderName)) {
            throw new TaskToolkitRegisterException("TaskToolkit builder already exists");
        }
    }

    @AllowedRoles({Role.TASK_OWNER, Role.ADMIN})
    @Override
    public void unregisterToolkitBuilder(TaskToolkitEntity toolkit) {
        activeToolkits.remove(toolkit);
        disabledToolkits.remove(toolkit);
    }

    @Override
    @AllowedRoles({Role.TASK_OWNER, Role.ADMIN})
    public void activateToolkitBuilder(TaskToolkitEntity toolkit) throws TaskToolkitRegisterException {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    @AllowedRoles({Role.TASK_OWNER, Role.ADMIN})
    public void deactivdateToolkitBuilder(TaskToolkitEntity toolkit) throws TaskToolkitRegisterException {
        //To change body of implemented methods use File | Settings | File Templates.
    }


    private void unregisterToolkitBuildersMap(Class<? extends TaskToolkit> clazz, final Map<String, TaskToolkit> builders) {
        Set<Map.Entry<String, TaskToolkit>> entries = builders.entrySet();
        for (Iterator<Map.Entry<String, TaskToolkit>> iterator = entries.iterator(); iterator.hasNext();) {
            Map.Entry<String, TaskToolkit> entry = iterator.next();
            if (entry.getValue().getClass().equals(clazz)) {
                iterator.remove();
            }
        }
    }


    private void moveEntry(String toolkitBuilderName, final Map<String, TaskToolkit> from, final Map<String, TaskToolkit> to) throws TaskToolkitRegisterException {
        if (to.containsKey(toolkitBuilderName)) {
            return;
        }
        if (from.containsKey(toolkitBuilderName)) {
            TaskToolkit builder = from.get(toolkitBuilderName);
            to.put(toolkitBuilderName, builder);
            from.remove(toolkitBuilderName);
        } else {
            throw new TaskToolkitRegisterException("Toolkit with name " + toolkitBuilderName + " doesn't exists");
        }
    }


    private void moveEntries(Comparable comparator, final Map<String, TaskToolkit> from, final Map<String, TaskToolkit> to) {
        Set<Map.Entry<String, TaskToolkit>> entries = from.entrySet();
        for (Iterator<Map.Entry<String, TaskToolkit>> iterator = entries.iterator(); iterator.hasNext();) {
            Map.Entry<String, TaskToolkit> entry = iterator.next();
            String s = entry.getKey();
            TaskToolkit value = entry.getValue();
            if (comparator.compareTo(value) == 0) {
                to.put(s, value);
                iterator.remove();
            }
        }
    }

    @AllowedRoles({Role.TASK_OWNER, Role.ADMIN})
    @Override
    public void deactivateToolkitBuilders(final Class<? extends TaskToolkit> clazz) {
        moveEntries(new CompareClasses(clazz), activeToolkits, disabledToolkits);
    }


    @AllowedRoles({Role.TASK_OWNER, Role.ADMIN})
    @Override
    public void activateAllToolkitBuilders() {
        moveAllEntries(disabledToolkits, activeToolkits);
    }

    @AllowedRoles({Role.TASK_OWNER, Role.ADMIN})
    @Override
    public void deactivateAllToolkitBuilders() {
        moveAllEntries(activeToolkits, disabledToolkits);
    }

    private void moveAllEntries(final Map<String, TaskToolkit> from, final Map<String, TaskToolkit> to) {
        Set<Map.Entry<String, TaskToolkit>> entries = from.entrySet();
        for (Iterator<Map.Entry<String, TaskToolkit>> iterator = entries.iterator(); iterator.hasNext();) {
            Map.Entry<String, TaskToolkit> entry = iterator.next();
            to.put(entry.getKey(), entry.getValue());
            iterator.remove();
        }
    }

    @Override
    public List<String> getToolkitBuilderNames(Class<? extends TaskToolkit> clazz) {
        Set<String> names = new HashSet<String>();

        Set<String> allNames = activeToolkits.keySet();

        for (String name : allNames) {
            if (activeToolkits.get(name).getClass().equals(clazz)) {
                names.add(name);
            }
        }
        return new ArrayList<String>(names);
    }

    @Override
    public TaskToolkitEntity getActiveToolkit(String name, Double version) {
        TaskToolkitEntity entity = null;
        TaskToolkit taskToolkit = activeToolkits.get(name);
        if (taskToolkit != null) {
            entity = new TaskToolkitEntity();
            entity.setToolkitName(name);
            entity.setToolkitClazz(taskToolkit.getClass().getCanonicalName());
            entity.setVersion(version);
            entity.setActive(true);
        }


        return entity;
    }


    @Override
    public List<String> getAllToolkitBuilderNames() {
        List<String> toolkitNames = new ArrayList<String>();
        toolkitNames.addAll(activeToolkits.keySet());
        toolkitNames.addAll(disabledToolkits.keySet());
        return toolkitNames;
    }

    @Override
    public List<String> getActiveToolkitsNames() {
        return new ArrayList<String>(activeToolkits.keySet());
    }

    @Override
    public boolean isToolkitRegistered(String builderName) {
        return activeToolkits.containsKey(builderName);
    }

    @AllowedRoles(Role.SYSTEM)
    @Override
    public void clearToolkits() {
        activeToolkits.clear();
        disabledToolkits.clear();
    }

    @Override
    public List<TaskToolkit> getSystemToolkits() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public List<TaskToolkit> getActiveToolkits() {

        throw new IllegalStateException("Not implemented yet");
    }

    @Override
    public List<TaskToolkit> getAllToolkits() {
        throw new IllegalStateException("Not implemented yet");
    }

    private static class CompareClasses implements Comparable {
        private final Class<? extends TaskToolkit> clazz;

        public CompareClasses(Class<? extends TaskToolkit> clazz) {
            this.clazz = clazz;
        }

        @Override
        public int compareTo(Object o) {
            return clazz.equals(o.getClass()) ? 0 : 1;
        }
    }
}
